package master.photo.service

import com.sun.image.codec.jpeg.JPEGCodec 
import com.sun.image.codec.jpeg.JPEGEncodeParam 
import com.sun.image.codec.jpeg.JPEGImageEncoder 

import java.awt.AlphaComposite;
import java.awt.Graphics2D 
import java.awt.Image 
import java.awt.RenderingHints 
import java.awt.image.BufferedImage 
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel 
import java.io.ByteArrayInputStream;

import javax.imageio.ImageIO

import org.springframework.stereotype.Component;

@Component
class GroovyImageResizer {
	
	byte[] scalePNG (byte[] inputImage, int width, int height) {
		File tempImgFile = createTempImageFile(inputImage);
		BufferedImage original = ImageIO.read(tempImgFile);
		BufferedImage resized = doScalePNG(original, 100, 100);
		
		// blur the image
		float ninth = 1.0f/9.0f;
		float[] blurKernel = [ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth, ninth];
		Map map = new HashMap();
		map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		RenderingHints hints = new RenderingHints(map);
		BufferedImageOp op = new ConvolveOp(new Kernel(3, 3, blurKernel), ConvolveOp.EDGE_NO_OP, hints);
		resized = op.filter(resized, null);
		
		resized = doScalePNG(resized, 16, 16);
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ImageIO.write(resized, 'png', baos);
		byte[] bytesOut = baos.toByteArray();
		
		tempImgFile.delete();
		
		return bytesOut;
	}
	
	private BufferedImage doScalePNG(BufferedImage original, int width, int height) {
		int type = original.getType() == 0? BufferedImage.TYPE_INT_ARGB : original.getType();
		BufferedImage resizedImage = new BufferedImage(width, height, type);
		
		Graphics2D g = resizedImage.createGraphics();
		g.setComposite(AlphaComposite.Src);
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.drawImage(original, 0, 0, width, height, null);
		g.dispose();
		
		return resizedImage; 
	}
	
	byte[] scaleJPG(byte[] inputImage, int width, int height) {
		ByteArrayInputStream bais = new ByteArrayInputStream(inputImage);
		InputStream imageStream = new BufferedInputStream(bais);
		Image image = (Image) ImageIO.read(imageStream); 
		
		int thumbWidth = width;
		int thumbHeight = height;        
		
		// Make sure the aspect ratio is maintained, so the image is not skewed
		double thumbRatio = (double)thumbWidth / (double)thumbHeight;
		int imageWidth = image.getWidth(null);
		int imageHeight = image.getHeight(null);
		double imageRatio = (double)imageWidth / (double)imageHeight;
		if (thumbRatio < imageRatio) {
			thumbHeight = (int)(thumbWidth / imageRatio);
		} else {
			thumbWidth = (int)(thumbHeight * imageRatio);
		}
		
		// Draw the scaled image
		BufferedImage thumbImage = new BufferedImage(thumbWidth, 
				thumbHeight, BufferedImage.TYPE_INT_RGB);
		Graphics2D graphics2D = thumbImage.createGraphics();
		graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		graphics2D.drawImage(image, 0, 0, thumbWidth, thumbHeight, null);
		
		// Write the scaled image to the output stream
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
		JPEGEncodeParam param = encoder.
				getDefaultJPEGEncodeParam(thumbImage);
		int quality = 100; // Use between 1 and 100, with 100 being highest quality
		quality = Math.max(0, Math.min(quality, 100));
		param.setQuality((float)quality / 100.0f, false);
		encoder.setJPEGEncodeParam(param);
		encoder.encode(thumbImage);        
		ImageIO.write(thumbImage, "jpg", out); 
		
		return out.toByteArray();        
	}
	
	private File createTempImageFile(byte[] input) {
		File temp = File.createTempFile("img_", ".tmp");
		bytesToFile(input, temp);
		return temp;
	}
	
	private byte[] getBytesFromFile(File file) throws IOException {
		InputStream is = new FileInputStream(file);
		// Get the size of the file
		long length = file.length();
		
		// You cannot create an array using a long type.
		// It needs to be an int type.
		// Before converting to an int type, check
		// to ensure that file is not larger than Integer.MAX_VALUE.
		if (length > Integer.MAX_VALUE) {
			// File is too large
		}
		
		// Create the byte array to hold the data
		byte[] bytes = new byte[(int)length];
		
		// Read in the bytes
		int offset = 0;
		int numRead = 0; 
		while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
			offset += numRead;
		}
		
		// Ensure all the bytes have been read in 
		if (offset < bytes.length) { 
			throw new IOException("Could not completely read file "+file.getName());
		}
		
		// Close the input stream and return bytes
		is.close();
		return bytes;
	}
	
	private File bytesToFile(byte[] bytes, String fileName) {
		FileOutputStream fos = new FileOutputStream(fileName);
		fos.write(bytes);
		fos.close();
	}
	
	private File bytesToFile(byte[] bytes, File f) {
		FileOutputStream fos = new FileOutputStream(f);
		fos.write(bytes);
		fos.close();
	}
	
	static void main(def args) {
		String filename = 'D:\\work\\img\\neytiri.jpg';
		
		File f = new File(filename);
		
		GroovyImageResizer resizer = new GroovyImageResizer();
		byte[] bytes = resizer.getBytesFromFile(f);
		
		if (filename.endsWith(".jpg")) {
			bytes = resizer.scaleJPG(bytes, 200, 200);
			resizer.bytesToFile(bytes, filename.replace(".jpg", "_resized.jpg"));
		} else if (filename.endsWith(".png")) {
			bytes = resizer.scalePNG(bytes, 16, 16);
			resizer.bytesToFile(bytes, filename.replace(".png", "_resized.png"));
		} else {
			System.out.println("Input format not supported. must be .jpg or .png");
		}
	}
	
}
